#include <utils/traceUtils/array.idc>
#include <utils/traceUtils/queue.idc>
#include <utils/traceUtils/registerValue.idc>
class ForRunToAddr{
    ForRunToAddr(){
        this.forSegmentName = Array(AR_STR, "forSegmentName");
        this.forStartAddr = Array(AR_LONG, "forStartAddr");
        this.forEndAddr = Array(AR_LONG, "forEndAddr");

        this.queueMax = Queue(AR_LONG, "queueMax", 500);
        this.queueMin = Queue(AR_LONG, "queueMin", 2);
        this.registerValue = RegisterValue();
    }

    ~ForRunToAddr(){}

    // for jump start ----------------------------------
    add(segmentNameParam, startAddr, endAddr){
        this.forSegmentName.add(segmentNameParam);
        this.forStartAddr.add(startAddr);
        this.forEndAddr.add(endAddr);
    }

    getForStartAddrIndex(startAddr){
        return this.forStartAddr.contain(startAddr);
    }

    getSegmentNameByIndex(index){
        return this.forSegmentName.get(index);
    }

    getForEndAddrByIndex(index){
        return this.forEndAddr.get(index);
    }

    forEcho(){
        auto i;
        Message("len: %d\n", this.forSegmentName.len);
        for(i = 0; i < this.forSegmentName.len; i = i + 1){
            Message("segmentName: %s, forStartAddr: 0x%x, forEndAddr: 0x%x \n", this.forSegmentName.get(i), this.forStartAddr.get(i), this.forEndAddr.get(i));
        }
        Message("\n");
    }
    // for jump end ----------------------------------

    // check for start -------------------------------
    addQueueAddr(addr){
        this.queueMax.push(addr);
        this.queueMin.push(addr);
    }

    checkFor(){
        return this.queueMax.checkFor(this.queueMin);
    }

    getForAddrArray(){
        this.forAddrArray = Array(AR_LONG, "forAddrArray");
        auto maxLen = this.queueMax.end - this.queueMax.start;
        auto minLen = this.queueMin.end - this.queueMin.start;

        auto i, maxTmp, minTmp, tmp, forCount;
        auto minFirst = GetArrayElement(AR_LONG, this.queueMin.ca, this.queueMin.end - 1);
        auto minSecond = GetArrayElement(AR_LONG, this.queueMin.ca, this.queueMin.end - 2);
        auto maxStartSearch = this.queueMax.end - 1 - minLen;
        forCount = 0;
        this.forAddrArray.add(minFirst);
        this.forAddrArray.add(minSecond);
        for(i = maxStartSearch; i >= this.queueMax.start + minLen - 1; i = i - 1){
            maxTmp = GetArrayElement(AR_LONG, this.queueMax.ca, i);
            tmp =  GetArrayElement(AR_LONG, this.queueMax.ca, i - 1);
            if(minFirst == maxTmp && minSecond == tmp){
                break;
            }

            this.forAddrArray.add(maxTmp);
        }
        return this.forAddrArray;
    }

    getJumpAddrArray(){
        this.getForAddrArray();
        // this.forAddrArray.arrayEcho();
        this.forJumpAddrArray = Array(AR_LONG, "forJumpAddrArray");

        auto i, maxTmp, mnem, inst, disAsm, addr, tmpIndex, bkAddrStr, bkAddr, tmpBkAddr;
        for(i = 0; i < this.forAddrArray.len; i = i + 1){
            addr = this.forAddrArray.get(i);
            mnem = GetMnem(addr);
            if(strstr(mnem, "B") == -1){
                continue;
            }

            if(mnem == "SUB" || mnem == "BKPT" || mnem == "BIC" || mnem == "QSUB" || mnem == "QDSUB" || mnem == "RSB" || mnem == "SBC"){
                continue;
            }

            tmpBkAddr = addr + 4;
            if(this.forAddrArray.contain(tmpBkAddr) == -1){
                this.forJumpAddrArray.add(tmpBkAddr);
            }

            inst = FindCode(PrevAddr(addr), SEARCH_DOWN | SEARCH_NEXT);
            disAsm = GetDisasm(inst);

            tmpIndex = strstr(disAsm, "loc_");
            if(tmpIndex == -1){
                continue;
            }

            bkAddrStr = substr(disAsm, tmpIndex + 4, tmpIndex + 14);
            bkAddr = xtol(bkAddrStr);
            if(this.forAddrArray.contain(bkAddr) == -1){
                this.forJumpAddrArray.add(bkAddr);
            }
        }
        return this.forJumpAddrArray;
    }

    addForJumpAddrBpt(){
        this.getJumpAddrArray();
        auto i, addr;
        for(i = 0; i < this.forJumpAddrArray.len; i = i + 1){
            addr = this.forJumpAddrArray.get(i);
            Message("add bpt 0x%x\n", addr);
            Sleep(100);
            AddBpt(addr);
        }

        Sleep(1000);
        addr = this.registerValue.getAddr("X30");
        RunTo(addr);
        GetDebuggerEvent(WFNE_ANY | WFNE_CONT, -1);
        Sleep(1000);

        for(i = 0; i < this.forJumpAddrArray.len; i = i + 1){
            addr = this.forJumpAddrArray.get(i);
            Message("del bpt 0x%x\n", addr);
            Sleep(100);
            DelBpt(addr);
        }
        Sleep(100);
        this.queueMax.initQueue();
    }
    // check for end ---------------------------------
}